﻿#pragma once

namespace fast_io
{

template <::std::integral ch_type>
class basic_win32_crypt_gen_random_io_observer
{
public:
	using native_handle_type = ::std::size_t;
	using input_char_type = ch_type;
	native_handle_type hprov{};
	inline constexpr native_handle_type native_handle() const noexcept
	{
		return hprov;
	}
	inline constexpr native_handle_type release() noexcept
	{
		auto tmp{hprov};
		hprov = 0;
		return tmp;
	}
};

template <::std::integral char_type>
inline constexpr basic_win32_crypt_gen_random_io_observer<char_type>
input_stream_ref_define(basic_win32_crypt_gen_random_io_observer<char_type> giob) noexcept
{
	return giob;
}

template <::std::integral char_type>
inline constexpr void
input_stream_require_secure_clear_define(basic_win32_crypt_gen_random_io_observer<char_type>) noexcept
{
}

namespace win32::details
{

inline ::std::byte *win32_crypt_gen_random_some_impl(::std::size_t hprov, ::std::byte *first, ::std::byte *last)
{
	if constexpr (sizeof(::std::size_t) <= sizeof(::std::uint_least32_t))
	{
		::std::size_t sz{static_cast<::std::size_t>(last - first)};
		if (!::fast_io::win32::CryptGenRandom(hprov, static_cast<::std::uint_least32_t>(sz),
											  reinterpret_cast<char unsigned *>(first)))
		{
			throw_win32_error();
		}
		return last;
	}
	else
	{
		constexpr ::std::size_t uintleast32mx{
			static_cast<::std::size_t>(::std::numeric_limits<::std::uint_least32_t>::max())};
		while (first != last)
		{
			::std::size_t toreadthisround{static_cast<::std::size_t>(last - first)};
			if (uintleast32mx < toreadthisround)
			{
				toreadthisround = uintleast32mx;
			}
			if (!::fast_io::win32::CryptGenRandom(hprov, static_cast<::std::uint_least32_t>(toreadthisround),
												  reinterpret_cast<char unsigned *>(first)))
			{
				return first;
			}
			first += toreadthisround;
		}
		return last;
	}
}

inline void win32_crypt_gen_random_all_impl(::std::size_t hprov, ::std::byte *first, ::std::byte *last)
{
	auto ret{win32_crypt_gen_random_some_impl(hprov, first, last)};
	if constexpr (sizeof(::std::uint_least32_t) < sizeof(::std::size_t))
	{
		if (ret != last)
		{
			throw_win32_error();
		}
	}
}

} // namespace win32::details

template <::std::integral char_type>
	requires(sizeof(::std::uint_least32_t) < sizeof(::std::size_t))
inline ::std::byte *read_some_bytes_underflow_define(basic_win32_crypt_gen_random_io_observer<char_type> bcgiob,
													 ::std::byte *first, ::std::byte *last)
{
	return ::fast_io::win32::details::win32_crypt_gen_random_some_impl(bcgiob.hprov, first, last);
}

template <::std::integral char_type>
inline void read_all_bytes_underflow_define(basic_win32_crypt_gen_random_io_observer<char_type> bcgiob,
											::std::byte *first, ::std::byte *last)
{
	::fast_io::win32::details::win32_crypt_gen_random_all_impl(bcgiob.hprov, first, last);
}

template <win32_family family, ::std::integral ch_type>
class basic_win32_family_crypt_gen_random_file : public basic_win32_crypt_gen_random_io_observer<ch_type>
{
public:
	using native_handle_type = ::std::size_t;
	using input_char_type = ch_type;
	basic_win32_family_crypt_gen_random_file()
		: basic_win32_crypt_gen_random_io_observer<ch_type>{
			  ::fast_io::details::win32::crypt_acquire_context_fallback<family>()}
	{
	}
	explicit constexpr basic_win32_family_crypt_gen_random_file(::std::nullptr_t)
		: basic_win32_crypt_gen_random_io_observer<ch_type>{}
	{
	}
	basic_win32_family_crypt_gen_random_file(basic_win32_family_crypt_gen_random_file const &) = delete;
	basic_win32_family_crypt_gen_random_file &operator=(basic_win32_family_crypt_gen_random_file const &) = delete;
	constexpr basic_win32_family_crypt_gen_random_file(
		basic_win32_family_crypt_gen_random_file &&__restrict other) noexcept
		: basic_win32_crypt_gen_random_io_observer<ch_type>{other.hprov}
	{
		other.hprov = 0;
	}
	basic_win32_family_crypt_gen_random_file &
	operator=(basic_win32_family_crypt_gen_random_file &&__restrict other) noexcept
	{
		if (this->hprov) [[likely]]
		{
			::fast_io::win32::CryptReleaseContext(this->hprov, 0);
		}
		this->hprov = other.hprov;
		other.hprov = 0;
		return *this;
	}

	~basic_win32_family_crypt_gen_random_file()
	{
		if (!this->hprov) [[likely]]
		{
			::fast_io::win32::CryptReleaseContext(this->hprov, 0);
		}
	}
	void close()
	{
		if (this->hprov) [[likely]]
		{
			if (!::fast_io::win32::CryptReleaseContext(this->hprov, 0))
			{
				throw_win32_error();
			}
		}
	}
};

template <::std::integral char_type>
using basic_win32_crypt_gen_random_file_9xa =
	basic_win32_family_crypt_gen_random_file<win32_family::ansi_9x, char_type>;
template <::std::integral char_type>
using basic_win32_crypt_gen_random_file_ntw =
	basic_win32_family_crypt_gen_random_file<win32_family::wide_nt, char_type>;
template <::std::integral char_type>
using basic_win32_crypt_gen_random_file = basic_win32_family_crypt_gen_random_file<win32_family::native, char_type>;

} // namespace fast_io
